﻿using IndustrialCommunication.Interfaces;
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace IndustrialCommunication.Core
{
    public  class SocketClient : IClient
    {
        protected ISocketOptions Options { get; set; }
        protected Socket CoreSocket { get; private set; }
        public void Connect()
        {
            CoreSocket = new Socket(Options.AddressFamily, Options.SocketType, Options.ProtocolType);
            if (Options.Server == null)
            {
                throw new ArgumentNullException(nameof(Options.Server));
            }
            var connectDone = new ManualResetEvent(false);
            Exception socketException = null;
            var isConnected = false;
            CoreSocket.BeginConnect(Options.Server, ar =>
            {
                try
                {
                    var client = ar.AsyncState as Socket;
                    if (client != null)
                    {
                        isConnected = true;
                        client.EndConnect(ar);
                    }
                }
                catch (Exception ex)
                {
                    socketException = ex;
                }
                finally
                {
                    connectDone.Set();
                }

            }, CoreSocket);
            if(connectDone.WaitOne(Options.ConnectTimeout ?? 10000, false))
            {
                if (!isConnected)
                {
                    throw socketException;
                }
            }
            else
            {
                CoreSocket.Close();
                throw new TimeoutException("socket连接超时");
            }
        }

        public Task ConnectAsync()
        {
            return Task.Run(() =>Connect());
        }

        public void Disconnect()
        {
            CoreSocket?.Close();
        }

        public Task DisconnectAsync()
        {
            return Task.Run(() => Disconnect());
        }

        public void Dispose()
        {
            throw new NotImplementedException();
        }

        public byte[] Receive()
        {
            if (!Options.ReceiveLength.HasValue)
            {
                var array = new byte[2048];
                CoreSocket.Receive(array);
                return array;
            }
            else
            {
                var array = new byte[Options.ReceiveLength.Value];
                var index = 0;
                while (index<Options.ReceiveLength.Value)
                {
                    var size = Math.Min(Options.ReceiveLength.Value - index, 16384);
                    int num = CoreSocket.Receive(array, index, size, SocketFlags.None);
                    index += num;
                    if (num==0)
                    {
                        break;
                    }
                }
                return array;
            }
        }

        public Task<byte[]> ReceiveAsync()
        {
            return Task.Factory.StartNew(() => Receive());
        }

        public void Send(byte[] data)
        {
            if (data==null)
            {
                return;
            }
            try
            {
                var dataLength = 0;
                var index = 0;
                if (Options.SendLength.HasValue)
                {
                    dataLength = Options.SendLength.Value;
                }
                else
                {
                    dataLength = data.Length;
                }
                do
                {
                    int num = CoreSocket.Send(data, index, dataLength - index, SocketFlags.None);
                    index += num;
                } while (index<dataLength);
            }
            catch (Exception ex)
            {
                CoreSocket?.Close();
            }
        }
        public Task SendAsync(byte[] data)
        {
            return Task.Factory.StartNew(() => Send(data));
        }
    }
}
